import com.google.common.util.concurrent.ThreadFactoryBuilder;
import game.Client;
import game.ClientManager;
import net.message.MsgProcessorManager;
import util.ConfPropertiesUtil;
import util.LogUtil;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.*;

public class MainClient {

    public static void main(String[] args) {
        ConfPropertiesUtil.getInstance().init();//初始化配置
        MsgProcessorManager.getInstance().init("game.handler");//初始化协议处理
        final int threadPoolSize = Math.max(1, Runtime.getRuntime().availableProcessors());
        ClientManager.getInstance().init(threadPoolSize);//初始化客户端管理器
        LogUtil.game.info("*********************模拟客户端启动***********************************");
        LogUtil.game.info("服务器地址:" + ConfPropertiesUtil.getInstance()._IP + ":" + ConfPropertiesUtil.getInstance()._PORT);
        LogUtil.game.info("逻辑线程数:" + threadPoolSize + "  登录线程池数:" + threadPoolSize + "  总登录数量:" + ConfPropertiesUtil.getInstance()._CLIENT_NUM + "  每秒登录数量:" + ConfPropertiesUtil.getInstance()._SECOND_LOGIN_NUM);
        LogUtil.game.info("登录token范围:" + ConfPropertiesUtil.getInstance()._CLIENT_TOKEN_PREFIX + ConfPropertiesUtil.getInstance()._CLIENT_BEGIN_NUN + "————" + ConfPropertiesUtil.getInstance()._CLIENT_TOKEN_PREFIX + (ConfPropertiesUtil.getInstance()._CLIENT_BEGIN_NUN + ConfPropertiesUtil.getInstance()._CLIENT_NUM - 1));
        LogUtil.game.info("执行行为:" + ConfPropertiesUtil.getInstance()._ACTION_LIST);
        LogUtil.game.info("*********************模拟客户端启动***********************************");
        //线程池处理登录：Google guava 工具类 提供的 ThreadFactoryBuilder ,使用链式方法创建
        ThreadFactory guavaThreadFactory = new ThreadFactoryBuilder().setNameFormat("client-pool").build();
        ExecutorService exec = new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(4096), guavaThreadFactory);
        final CountDownLatch latch = new CountDownLatch(ConfPropertiesUtil.getInstance()._CLIENT_NUM);

        final int timerNumber = ConfPropertiesUtil.getInstance()._CLIENT_NUM / ConfPropertiesUtil.getInstance()._SECOND_LOGIN_NUM + 1;//设置运行次数
        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            int count = 0;//从0开始计数，每运行一次timertask次数加一，运行制定次数后结束。

            @Override
            public void run() {
                if (count < timerNumber) {
                    final int begin = ConfPropertiesUtil.getInstance()._CLIENT_BEGIN_NUN + count * ConfPropertiesUtil.getInstance()._SECOND_LOGIN_NUM;
                    final int end = ConfPropertiesUtil.getInstance()._CLIENT_BEGIN_NUN + Math.min(ConfPropertiesUtil.getInstance()._CLIENT_NUM, (count + 1) * ConfPropertiesUtil.getInstance()._SECOND_LOGIN_NUM);
                    for (int index = begin; index < end; index++) {
                        final Client client = new Client(index);
                        ClientManager.getInstance().addClient(client);//分配至线程中tick执行逻辑
                        exec.execute(() -> {
                            client.Start();
                            latch.countDown();//等待所有任务执行完后销毁线程池
                        });//线程池用来登录
                    }
                } else {
                    timer.cancel();
                }
                count++;
            }
        };
        timer.schedule(task, 0, 1000);//每隔1000毫秒即一秒运行一次该程序
        /**
         * 进程结束前最后的处理
         * 是 JVM 销毁前要执行的一个线程
         * kill -9 pid时不会被调用
         * 推荐使用kill -15 pid
         * 若有多个hook，JVM调用顺序随机
         * System.exit(0);也可触发钩子
         */
        Runtime.getRuntime().addShutdownHook(new Thread("shutdown-hook") {
            @Override
            public void run() {
                LogUtil.game.info("优雅的关闭客户端begin");
                ClientManager.getInstance().close();
                LogUtil.game.info("优雅的关闭客户端success");
            }
        });
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        exec.shutdown();
        LogUtil.game.info("客户端启动 完成");
    }
}
